import Math2D from "./Math2D";
import Vector2 from "./Vector2";

export default class Matrix2D {
    public static EPSILON: number = 0.00001;

    public values: Float32Array;

    public get xAxis ( ) : Vector2 {
        return Vector2 . create ( this . values [ 0 ] , this . values [ 1 ] ) ;
    }

    public get yAxis ( ) : Vector2 {
        return Vector2 . create ( this . values [ 2 ] , this . values [ 3 ] ) ;
    }

    public get origin ( ) : Vector2 {
        return Vector2 . create ( this . values [ 4 ] , this . values [ 5 ] )
    }

    public constructor(a: number = 1, b: number = 0, c: number = 0, d: number = 1, x: number = 0, y: number = 0) {
        this.values = new Float32Array([a, b, c, d, x, y]);
    }

    public static create(a: number = 1, b: number = 0, c: number = 0, d: number = 1, x: number = 0, y: number = 0): Matrix2D {
        return new Matrix2D(a, b, c, d, x, y);
    }

    public static copy(src: Matrix2D, result: Matrix2D | null = null): Matrix2D {
        if (result === null) result = new Matrix2D();
        result.values[0] = src.values[0];
        result.values[1] = src.values[1];
        result.values[2] = src.values[2];
        result.values[3] = src.values[3];
        result.values[4] = src.values[4];
        result.values[5] = src.values[5];
        return result;
    }

    /**
     * *矩阵的乘法
     * @param left 
     * @param right 
     * @param result 
     */
    public static multiply(left: Matrix2D, right: Matrix2D, result: Matrix2D | null = null): Matrix2D {
        if (result === null) result = new Matrix2D();
        let a0: number = left.values[0];
        let a1: number = left.values[1];
        let a2: number = left.values[2];
        let a3: number = left.values[3];
        let a4: number = left.values[4];
        let a5: number = left.values[5];
        let b0: number = right.values[0];
        let b1: number = right.values[1];
        let b2: number = right.values[2];
        let b3: number = right.values[3];
        let b4: number = right.values[4];
        let b5: number = right.values[5];

        result.values[0] = a0 * b0 + a2 * b1;
        result.values[1] = a1 * b0 + a3 * b1;
        result.values[2] = a0 * b2 + a2 * b3;
        result.values[3] = a1 * b2 + a3 * b3;
        result.values[4] = a0 * b4 + a2 * b5 + a4;
        result.values[5] = a1 * b4 + a3 * b5 + a5;
        return result;
    }

    /**
     * *单位矩阵
     */
    public indentity(): void {
        this.values[0] = 1.0;
        this.values[1] = 0.0;
        this.values[2] = 0.0;
        this.values[3] = 1.0;
        this.values[4] = 0.0;
        this.values[5] = 0.0;
    }

    /**
     * *计算矩阵的行列式
     * @param matrix 
     */
    public static determinant(matrix: Matrix2D): number {
        return matrix.values[0] * matrix.values[3] - matrix.values[2] * matrix.values[1];
    }

    /**
     * *求矩阵src的逆矩阵，将结算后的逆矩阵从result参数中输出
     * *如果有逆矩阵，返回true，否则返回false
     * *下面的代码中使用：伴随矩阵 / 行列式 的方式来求矩阵的逆
     * @param src 
     * @param result 
     */
    public static invert(src: Matrix2D, result: Matrix2D): boolean {
        // 1.获取要求逆矩阵的行列式
        let det: number = Matrix2D.determinant(src);
        // 2.如果行列式为0，则无法求逆，直接返回false
        if (Math2D.isEquals(det, 0)) {
            return false;
        }
        // 3.使用：伴随矩阵 / 行列式 的算法来求矩阵的逆
        // 由于计算机中除法效率较低，先进行一次除法，求行列式的倒数
        // 后面代码就可以直接乘以行列式的倒数，这样避免了多次除法操作
        det = 1.0 / det;
        // 4.下面的代码中，*det之前的代码都是求标准伴随矩阵的源码
        // 最后乘以行列式的倒数，获得每个元素的正确数值
        result.values[0] = src.values[3] * det;
        result.values[1] = -src.values[1] * det;
        result.values[2] = -src.values[2] * det;
        result.values[3] = src.values[0] * det;
        result.values[4] = (src.values[2] * src.values[5] - src.values[3] * src.values[4]) * det;
        result.values[5] = (src.values[1] * src.values[4] - src.values[0] * src.values[5]) * det;
        // 如果矩阵求逆成功，返回true
        return true;
    }

    /**
     * *矩阵和向量的相乘，返回一个向量
     * @param matrix 
     * @param pt 
     * @param result 
     */
    public static transform(matrix: Matrix2D, pt: Vector2, result: Vector2 | null = null): Vector2 {
        if (result === null) result = Vector2.create();
        result.values[0] = matrix.values[0] * pt.values[0] + matrix.values[2] * pt.values[1] + matrix.values[4];
        result.values[1] = matrix.values[1] * pt.values[0] + matrix.values[3] * pt.values[1] + matrix.values[5];
        return result;
    }

    /**
     * *平移矩阵
     * @param tx 
     * @param ty 
     * @param result 
     */
    public static makeTranslation(tx: number, ty: number, result: Matrix2D | null = null): Matrix2D {
        if (result === null) result = new Matrix2D();
        result.values[0] = 1;
        result.values[1] = 0;
        result.values[2] = 0;
        result.values[3] = 1;
        // 会看到平移矩阵只需要设置第4个和第5个元素的值
        result.values[4] = tx;
        result.values[5] = ty;
        return result;
    }

    /**
     * *缩放矩阵
     * @param sx 
     * @param sy 
     * @param result 
     */
    public static makeScale(sx: number, sy: number, result: Matrix2D | null = null): Matrix2D {
        if (Math2D.isEquals(sx, 0) || Math2D.isEquals(sy, 0)) {
            alert("X轴或Y轴缩放系数为0");
            throw new Error("X轴或Y轴缩放系数为0");
        }
        if (result === null) result = new Matrix2D();
        result.values[0] = sx;
        result.values[1] = 0;
        result.values[2] = 0;
        result.values[3] = sy;
        result.values[4] = 0;
        result.values[5] = 0;
        return result;
    }

    /**
     * *旋转矩阵
     * @param radians 
     * @param result 
     */
    public static makeRotation(radians: number, result: Matrix2D | null = null): Matrix2D {
        if (result === null) result = new Matrix2D();
        let s: number = Math.sin(radians), c: number = Math.cos(radians);
        result.values[0] = c;
        result.values[1] = s;
        result.values[2] = -s;
        result.values[3] = c;
        result.values[4] = 0;
        result.values[5] = 0;
        return result;
    }

    /**
     * *会修改this指向的数据
     */
    public onlyRotationMatrixInvert(): Matrix2D {
        let s: number = this.values[1];
        // 矩阵的第1个元素和第2个元素值交换
        this.values[1] = this.values[2];
        this.values[2] = s;
        return this;
    }

    /**
     * *从两个单位向量构造旋转矩阵
     * @param v1 
     * @param v2 
     * @param norm 是否需要normlize两个向量
     * @param result 
     */
    public static makeRotationFromVectors(v1: Vector2, v2: Vector2, norm: boolean = false, result: Matrix2D | null = null): Matrix2D {
        if (result === null) result = new Matrix2D();
        result.values[0] = Vector2.cosAngle(v1, v2, norm);
        result.values[1] = Vector2.sinAngle(v1, v2, norm);
        result.values[2] = -Vector2.sinAngle(v1, v2, norm);
        result.values[3] = Vector2.cosAngle(v1, v2, norm);
        result.values[4] = 0;
        result.values[5] = 0;
        return result;
    }
}